//
// Created by tcj on 2022/7/17.
//

#include "webserver.h"

namespace tcj{
    webserver::webserver(int port, int trigMode, int timeoutMs, bool optLinger, int threadNum)
            :port_(port)
            ,openLinger_(optLinger)
            ,timeoutMs_(timeoutMs)
            ,isClose_(false)
            ,timer_(new TimerManager())
            ,threadpool_(new ThreadPool(threadNum))
            ,epoller_(new epoller()){
        srcDir_ = getcwd(nullptr,256);

        assert(srcDir_);
        strncat(srcDir_,"/netweb/",16);
        HTTPconnection::userCount =0;
        HTTPconnection::srcDir = srcDir_;

        initEventMode_(trigMode);
        if (!initScoet()) isClose_= false;
    }

    webserver::~webserver() {
        close(listenFd_);
        isClose_ = true;
        free(srcDir_);
    }

    void webserver::initEventMode_(int trigMode) {
        listenEvent_ = EPOLLRDHUP;
        connectEvent_ = EPOLLONESHOT | EPOLLRDHUP;
        switch (trigMode)
        {
            case 0:
                break;
            case 1:
                connectEvent_ |= EPOLLET;
                break;
            case 2:
                listenEvent_ |= EPOLLET;
                break;
            case 3:
                listenEvent_ |= EPOLLET;
                connectEvent_ |= EPOLLET;
                break;
            default:
                listenEvent_ |= EPOLLET;
                connectEvent_ |= EPOLLET;
                break;
        }
        HTTPconnection::isET = (connectEvent_ & EPOLLET);
    }

//    void webserver::initEventMode_(int trigMode) {
//        listenEvent_ = EPOLLRDHUP;
//        connectEvent_ = EPOLLONESHOT | EPOLLRDHUP;
//        switch (trigMode) {
//            case 0:
//                break;
//            case 1:
//                connectEvent_ |= EPOLLET;
//                break;
//            case 2:
//                listenEvent_ |= EPOLLET;
//                break;
//            case 3:
//                listenEvent_ | EPOLLET;
//                connectEvent_ |= EPOLLET;
//                break;
//            default:
//                listenEvent_ |=  EPOLLET;
//                connectEvent_ |= EPOLLET;
//                break;
//        }
//        HTTPconnection::isET = (connectEvent_ & EPOLLET);
//    }

    void webserver::start() {
        int timeMs = -1;
        if (!isClose_){
            std::cout<<"======================";
            std::cout<<"Server start";
            std::cout<<"======================";
            std::cout<<std::endl;
        }
        while(!isClose_){
            if (timeoutMs_>0){
                timeMs = timer_->getNextHandle();
            }
            int eventCnt = epoller_->wait(timeMs);
            for (int i = 0; i < eventCnt; ++i) {
                int fd = epoller_->getEventFd(i);
                uint32_t events = epoller_->getEvents(i);

                if (fd == listenFd_){
                    handleListen();
                }else if(events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)){
                    assert(users_.count(fd)>0);
                    closeConn(&users_[fd]);
                }else if(events & EPOLLIN){
                    assert(users_.count(fd)>0);
                    handleRead_(&users_[fd]);
                }else if(events & EPOLLOUT){
                    assert(users_.count(fd)>0);
                    handleWrite_(&users_[fd]);
                }else{
                    std::cout<<"unexcepted events"<<std::endl;
                }
            }
        }
    }

    void webserver::sendError(int fd, const char *info) {
        assert(fd>0);
        int ret = send(fd,info, strlen(info),0);
        close(fd);
    }

    void webserver::closeConn(HTTPconnection *client) {
        assert(client);
        epoller_->delFd(client->getFd());
        client->closeHttpConn();
    }

    void webserver::addClientCOnnection(int fd, sockaddr_in addr) {
        assert(fd>0);
        users_[fd].initHttpConn(fd,addr);
        if (timeoutMs_>0){
            timer_->addTimer(fd,timeoutMs_,std::bind(&webserver::closeConn,this,&users_[fd]));
        }
        epoller_->addFd(fd,EPOLLIN|connectEvent_);
        setFdNonbock(fd);
    }


    void webserver::handleListen() {
        struct sockaddr_in addr;
        socklen_t  len = sizeof(addr);
        do {
            int fd = accept(listenFd_,(struct sockaddr *)&addr,&len);
            if (fd<=0) return;
            else if (HTTPconnection::userCount >= MAX_FD){
                sendError(fd,"werver busy");
                return;
            }
            addClientCOnnection(fd,addr);
        } while (listenEvent_ & EPOLLET);
    }

    void webserver::handleRead_(HTTPconnection *client) {
        assert(client);
        extenTime(client);
        threadpool_->submit(std::bind(&webserver::onRead,this,client));
    }


    void webserver::handleWrite_(HTTPconnection *client) {
        assert(client);
        extenTime(client);
        threadpool_->submit(std::bind(&webserver::onWrite,this,client));
    }

    void webserver::extenTime(HTTPconnection *client) {
        assert(client);
        if (timeoutMs_>0){
            timer_->update(client->getFd(),timeoutMs_);
        }
    }

    void webserver::onRead(HTTPconnection *client) {
        assert(client);
        int ret=-1;
        int readErrno = 0;
        ret = client->readBuffer(&readErrno);
        cout<<"ret is "<<ret<<endl;
        if (ret<0 && readErrno != EAGAIN){
            std::cout<<"do not read data!"<<std::endl;
            closeConn(client);
            return;
        }
        onProcess(client);
    }

    void webserver::onProcess(HTTPconnection *client) {
        if (client->handleHttpConn()){
            epoller_->modFd(client->getFd(),connectEvent_ | EPOLLOUT);
        }else{
            epoller_->modFd(client->getFd(),connectEvent_ | EPOLLIN);
        }
    }

    void webserver::onWrite(HTTPconnection *client) {
        assert(client);
        int ret =-1;
        int writeErrno =0;
        ret = client->writeBuffer(&writeErrno);
        if (client->writeBytes() == 0){
            if (client->isKeepLive()){
                onProcess(client);
                return ;
            }
        }else if (ret<0){
            if (writeErrno == EAGAIN){
                epoller_->modFd(client->getFd(),connectEvent_ | EPOLLOUT);
                return;
            }
        }
        closeConn(client);
    }

    bool webserver::initScoet() {
        int ret;
        struct sockaddr_in addr;
        if (port_>65535 || port_<1024){
            return false;
        }

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        addr.sin_port = htons(port_);
        struct linger  optlinger = { 0 };
        if (openLinger_){
            optlinger.l_onoff =1;
            optlinger.l_linger =1;
        }
        listenFd_ = socket(AF_INET,SOCK_STREAM,0);
        if (listenFd_<0) return false;

        ret = setsockopt(listenFd_,SOL_SOCKET,SO_LINGER,&optlinger,sizeof (optlinger));
        if (ret<0){
            close(listenFd_);
            return false;
        }

        int optval=1;
        ret = setsockopt(listenFd_,SOL_SOCKET,SO_REUSEADDR,(const void*)&optval,sizeof (int));
        if (ret == -1){
            close(listenFd_);
            return false;
        }

        ret = bind(listenFd_, (struct sockaddr*)&addr,sizeof(addr));
        if (ret<0){
            close(listenFd_);
            return false;
        }

        ret = listen(listenFd_,0);
        if (ret<0){
            close(listenFd_);
            return false;
        }

        ret = epoller_->addFd(listenFd_,listenEvent_|EPOLLIN);
        if (ret==0){
            close(listenFd_);
            return false;
        }
        setFdNonbock(listenFd_);
        return true;
    }

    int webserver::setFdNonbock(int fd) {
        assert(fd>0);
        return fcntl(fd,F_SETFL, fcntl(fd,F_GETFD,0) | O_NONBLOCK);
    }
}